#include "timerbase.h"
#include <vector>

namespace wilson::sys
{
    timer_task::
    timer_task(::std::chrono::time_point<
                   ::std::chrono::system_clock
               > tt_timeout_point, 
               ::std::function<void()> callback)
        : tt_timeout_point{ tt_timeout_point }, 
          tt_callback{ ::std::move(callback) }
    {
    }

    timer_task::
    timer_task(const ::std::chrono::milliseconds& tt_timeout, 
               ::std::function<void()> callback)
        // Calculate the timeout point by `tt_timeout`
        : timer_task{ ::std::chrono::system_clock::now() + tt_timeout, 
                      ::std::move(callback) }
    {
    }

    ::std::strong_ordering 
    operator<=>(const timer_task& t1, const timer_task& t2) noexcept
    {
        return t1.timeout_point() <=> t2.timeout_point();
    }

    void 
    timer_task::
    do_callback() 
    { 
        if (tt_callback) [[likely]]
            return tt_callback(); 
    }

    timerbase::timerbase(timerbase&& other)
    {
        ::std::scoped_lock locks{ t_mutex, other.t_mutex };
        t_tasks = ::std::move(other.t_tasks);
    }

    void 
    timerbase::
    add_task(timer_task tt)
    {
        ::std::lock_guard lg{ t_mutex };
        t_tasks.emplace(::std::move(tt));
    }

    void 
    timerbase::
    add_task(::std::chrono::milliseconds timeout, 
             ::std::function<void()> callback)
    {
        add_task(timer_task{ timeout, ::std::move(callback) });
    }

    void
    timerbase::
    do_all_timeout()
    {
        ::std::vector<timer_task> timeout_tasks;
        ::std::unique_lock lock{ t_mutex, ::std::defer_lock };

        /* Only one thread is allowed to do the current operation
         * When the application for locking is unsuccessful, 
         * it means that there is another thread is operating.
         * So, we can just skip it.
         */
        if (lock.try_lock())
        {
            retrive_timeout_tasks(::std::back_inserter(timeout_tasks));
            lock.unlock();
            for (auto& task : timeout_tasks)
                task();
        }
    }

    tt_timeout_point_t 
    timerbase::pioneer() const
    {
        ::std::lock_guard lock{ t_mutex };
        if (t_tasks.empty()) return tt_timeout_point_t::max();
        return t_tasks.begin()->timeout_point();
    }
}
